import { deleteNode, fetchNodes, searchNode, type TreeNode } from "@/mock-network";

export class TreeModel {

    private _data: TreeNode[] = [];
    private _selectedNode: TreeNode | null = null;
    private _expandedKeys = new Set<string>();

    get data() {
        return [...this._data];
    }

    get selectedNode() {
        return this._selectedNode;
    }

    get expandedKeys() {
        return [...this._expandedKeys.values()];
    }

    get loadedKeys() {
        return [...this._expandedKeys.values()];
    }

    updateSelectNode(node: TreeNode) {
        this._selectedNode = node;
    }

    async init() {
        const root = await fetchNodes();
        this._data = root;
    }

    /**
     * 展开事件
     * @param node 
     */
    async expand(node: TreeNode) {
        const children = await fetchNodes(node.key);
        this.updateNodes(this._data, node.key, children);
        this._selectedNode = node;
        this._expandedKeys.add(node.key);
    }

    private updateNodes(nodes: TreeNode[], nodeKey: string, children: TreeNode[] = []) {
        for (const node of nodes) {
            if (node.key === nodeKey) {
                node.children = children;
                return;
            }

            if (node.children?.length) {
                this.updateNodes(node.children, nodeKey, children);
            }
        }
    }

    /**
     * 折叠事件
     * @param info 
     */
    async collapsed(node: TreeNode) {
        if (node.children?.length) {
            for (const child of node.children) {
                this.collapsed(child);
            }
        }
        this.removeChildren(node);
        this._selectedNode = node;
        this._expandedKeys.delete(node.key);
    }

    private removeChildren(node: TreeNode) {
        node.children = [];
    }

    /**
     * 删除节点
     * @param node 
     */
    async delete() {
        const selectedNode = this.selectedNode;
        if (selectedNode === null) {
            return;
        }

        await deleteNode(selectedNode.key);

        const parentNode = this.findParentNode(this._data, selectedNode.key);
        if (!parentNode) {
            // 根节点被移除
            this._selectedNode = null;
            this._expandedKeys.clear();
            return;
        }

        // 从当前父节点中移除该元素
        const childIndex = parentNode.children!.findIndex(({ key }) => key === selectedNode.key)
        parentNode.children!.splice(childIndex, 1)

        // 选中父节点
        this._selectedNode = parentNode;

        // 折叠父节点的兄弟节点
        const siblingNodes = this.findSiblingNodes(this._data, parentNode.key) ?? [];
        siblingNodes.forEach(({ key }) => {
            this._expandedKeys.delete(key)
        });
    }

    private findParentNode(nodes: TreeNode[], nodeKey: string, parentNode?: TreeNode): TreeNode | undefined {
        for (const node of nodes) {
            if (node.key === nodeKey) {
                return parentNode;
            }

            if (node.children?.length) {
                const parentNode = this.findParentNode(node.children, nodeKey, node);
                if (parentNode) {
                    return parentNode;
                }
            }
        }
        return;
    }

    private findSiblingNodes(nodes: TreeNode[], nodeKey: string): TreeNode[] | undefined {
        if (nodes.some(({ key }) => key === nodeKey)) {
            return nodes.filter(({ key }) => key !== nodeKey);
        }

        for (const { children } of nodes) {
            if (children?.length) {
                const siblingNodes = this.findSiblingNodes(children, nodeKey);
                if (siblingNodes) {
                    return siblingNodes;
                }
            }
        }
        return;
    }

    async search(searchValue: string) {
        const result = await searchNode(searchValue);
        if (!result) {
            return false;
        }

        const { node, paths } = result;
        this._selectedNode = node;

        for (const path of paths) {
            if (this._expandedKeys.has(path)) {
                continue;   // 已经加载完成的话，不需要处理
            }

            const children = await fetchNodes(path);
            this.updateNodes(this._data, path, children);
            this._expandedKeys.add(path)
        }
        return true;
    }
}